unit GLGUIEvent;

interface

uses
  SysUtils,
  Classes,
  GLSGenerics,
  VectorGeometry,
  GLCrossPlatform;

type
  TGLGUIEvent = class;
  TGLGUIEventPhase = (epWorking, epStoped);
  TGLGUIEventClass = class of TGLGUIEvent;
  TGLGUIEventType = String;
  TGLGUIEventsList = GList<TGLGUIEvent>;

  TGLGUIEventListener = procedure(AEvent: TGLGUIEvent) of Object;
{$IFNDEF FPC}
  TGLGUIEventListener<T> = procedure(AEvent: T) of Object;
{$ENDIF}

  TGLGUIEventListenerBind = class
  public
{$IFNDEF FPC}
    Listener: TGLGUIEventListener<TGLGUIEvent>;
{$ELSE}
    Listener: TGLGUIEventListener;
{$ENDIF}
    Sender: TObject;
  end;

  TGLGUIEventListenersList = GList<TGLGUIEventListenerBind>;
  TGLGUIMouseEvent = class;

  TGLGUIEvent = class
  protected
    FType: TGLGUIEventType;
    FPhase: TGLGUIEventPhase;
    FSender: TObject;
    FListeners: TGLGUIEventListenersList;
    FIgnoreState: Boolean;
  public
    constructor Create(); virtual;
    procedure DispatchEvent(ASender: TObject); virtual;
    procedure AddListener(const AListener: TGLGUIEventListenerBind);
    procedure RemoveListener(const AListener: TGLGUIEventListener); virtual;
    procedure RemoveListeners(const ASender: TGLGUIEventListenersList); virtual;
    function ToString(): TGLGUIEventType; override;
    function AsMouseEvent: TGLGUIMouseEvent;
    function Working: Boolean; virtual;
    destructor Destroy; override;

    property EventType: String read FType write FType;
    property EventPhase: TGLGUIEventPhase read FPhase write FPhase;
    property Sender: TObject read FSender write FSender;
    property IgnoreState: Boolean read FIgnoreState write FIgnoreState
      default False;
  end;

  TGLGUISingleUserEvent = class(TGLGUIEvent)
    procedure DispatchEvent(ASender: TObject); override;
  end;

  TGLGUIMouseEvent = class(TGLGUIEvent)
  public
    ShiftState: TShiftState;
    Button: TGLMouseButton;
    X, Y: Integer;
    procedure Prepare; virtual;
  end;

  TGLGUIMouseDownEvent = class(TGLGUIMouseEvent)
  end;

  TGLGUIMouseUpEvent = class(TGLGUIMouseEvent)
  end;

  TGLGUIMouseMoveEvent = class(TGLGUIMouseEvent)
  end;

  TGLGUIMouseEnterEvent = class(TGLGUIMouseEvent)
  end;

  TGLGUIMouseLeaveEvent = class(TGLGUIMouseEvent)
  end;

  TGLGUIMouseWheelEvent = class(TGLGUIMouseEvent)
  public
    Delta: Integer;
  end;

  TGLGUIKeyEvent = class(TGLGUIEvent)
  public
    Key: WideChar;
    Code: Word;
    Capital: Boolean;
  end;

  TGLGUIKeyDownEvent = class(TGLGUIKeyEvent)
  end;

  TGLGUIKeyUpEvent = class(TGLGUIKeyEvent)
  end;

  TGLGUIProgressEvent = class(TGLGUISingleUserEvent)
  public
    DeltaTime: Double;
    NewTime: Double;
    procedure DispatchEvent(ASender: TObject); override;
  end;

function MouseDownEvent: TGLGUIMouseEvent;
function MouseUpEvent: TGLGUIMouseEvent;
function MouseMoveEvent: TGLGUIMouseEvent;
function MouseEnterEvent: TGLGUIMouseEvent;
function MouseLeaveEvent: TGLGUIMouseEvent;
function MouseWheelEvent: TGLGUIMouseWheelEvent;
function KeyDownEvent: TGLGUIKeyEvent;
function KeyUpEvent: TGLGUIKeyEvent;
function GUIChangeEvent: TGLGUIEvent;
function FontUpdateEvent: TGLGUIEvent;

implementation

uses
  uMain,
  GLGUIBase,
  GLGUIEventManager,
  GLGUIInputManager;

type
  TGLGUIObjectAccessProvider = class(TGLGUIObject)
  end;

const
  CMouseDown = 'TGLGUIMouseDownEvent';
  CMouseUp = 'TGLGUIMouseUpEvent';
  CMouseMove = 'TGLGUIMouseMoveEvent';
  CMouseEnter = 'TGLGUIMouseEnterEvent';
  CMouseLeave = 'TGLGUIMouseLeaveEvent';
  CMouseWheel = 'TGLGUIMouseWheelEvent';
  CKeyDown = 'TGLGUIKeyDownEvent';
  CKeyUp = 'TGLGUIKeyUpEvent';
  CProgress = 'TGLGUIProgressEvent';
  CGUIChange = 'GUIChangeEvent';
  CFontUpdate = 'FontUpdated';

var
  vMouseWheelEvent: TGLGUIMouseWheelEvent;
  vMouseDownEvent: TGLGUIMouseEvent;
  vMouseUpEvent: TGLGUIMouseEvent;
  vMouseMoveEvent: TGLGUIMouseEvent;
  vMouseEnterEvent: TGLGUIMouseEvent;
  vMouseLeaveEvent: TGLGUIMouseEvent;
  vKeyDownEvent: TGLGUIKeyEvent;
  vKeyUpEvent: TGLGUIKeyEvent;
  vGUIChangeEvent: TGLGUIEvent;
  vFontUpdateEvent: TGLGUIEvent;

function MouseDownEvent: TGLGUIMouseEvent;
begin
  if vMouseDownEvent = nil then
    with GUIEventManager do
      vMouseDownEvent := TGLGUIMouseEvent(GetOrCreateEvent(CMouseDown));
  Result := vMouseDownEvent;
end;

function MouseUpEvent: TGLGUIMouseEvent;
begin
  if vMouseUpEvent = nil then
    with GUIEventManager do
      vMouseUpEvent := TGLGUIMouseEvent(GetOrCreateEvent(CMouseUp));
  Result := vMouseUpEvent;
end;

function MouseMoveEvent: TGLGUIMouseEvent;
begin
  if vMouseMoveEvent = nil then
    with GUIEventManager do
      vMouseMoveEvent := TGLGUIMouseEvent(GetOrCreateEvent(CMouseMove));
  Result := vMouseMoveEvent;
end;

function MouseEnterEvent: TGLGUIMouseEvent;
begin
  if vMouseEnterEvent = nil then
    with GUIEventManager do
      vMouseEnterEvent := TGLGUIMouseEvent(GetOrCreateEvent(CMouseEnter));
  Result := vMouseEnterEvent;
end;

function MouseLeaveEvent: TGLGUIMouseEvent;
begin
  if vMouseLeaveEvent = nil then
    with GUIEventManager do
      vMouseLeaveEvent := TGLGUIMouseEvent(GetOrCreateEvent(CMouseLeave));
  Result := vMouseLeaveEvent;
end;

function MouseWheelEvent: TGLGUIMouseWheelEvent;
begin
  if vMouseWheelEvent = nil then
    with GUIEventManager do
      vMouseWheelEvent := TGLGUIMouseWheelEvent(GetOrCreateEvent(CMouseWheel));
  Result := vMouseWheelEvent;
end;

function KeyDownEvent: TGLGUIKeyEvent;
begin
  if vKeyDownEvent = nil then
    with GUIEventManager do
      vKeyDownEvent := TGLGUIKeyEvent(GetOrCreateEvent(CKeyDown));
  Result := vKeyDownEvent;
end;

function KeyUpEvent: TGLGUIKeyEvent;
begin
  if vKeyUpEvent = nil then
    with GUIEventManager do
      vKeyUpEvent := TGLGUIKeyEvent(GetOrCreateEvent(CKeyUp));
  Result := vKeyUpEvent;
end;

function GUIChangeEvent: TGLGUIEvent;
begin
  if vGUIChangeEvent = nil then
  begin
    with GUIEventManager do
      vGUIChangeEvent := GetOrCreateEvent(CGUIChange);
  end;
  Result := vGUIChangeEvent;
end;

function FontUpdateEvent: TGLGUIEvent;
begin
  if vFontUpdateEvent = nil then
  begin
    with GUIEventManager do
      vFontUpdateEvent := GetOrCreateEvent(CFontUpdate);
  end;
  Result := vFontUpdateEvent;
end;

constructor TGLGUIEvent.Create();
begin
  FListeners := TGLGUIEventListenersList.Create;
end;

function TGLGUIEvent.ToString(): TGLGUIEventType;
begin
  Result := FType;
end;

function TGLGUIEvent.AsMouseEvent: TGLGUIMouseEvent;
begin
  Result := Self as TGLGUIMouseEvent;
end;

function TGLGUIEvent.Working: Boolean;
begin
  Result := EventPhase = epWorking;
end;

procedure TGLGUIEvent.DispatchEvent(ASender: TObject);
var
  i: Integer;
begin
  FSender := ASender;
  for i := 0 to FListeners.Count - 1 do
    if FListeners.Items[i].Sender = FSender then
      FListeners.Items[i].Listener(Self);
end;

procedure TGLGUIEvent.AddListener(const AListener: TGLGUIEventListenerBind);
begin
  FListeners.Add(AListener);
end;

procedure TGLGUIEvent.RemoveListeners(const ASender: TGLGUIEventListenersList);
var
  i: Integer;
  tBind: TGLGUIEventListenerBind;
begin
  for i := 0 to ASender.Count - 1 do
  begin
    tBind := ASender.Items[i];
    if FListeners.Remove(tBind) > -1 then
      tBind.Free;
  end;
end;

procedure TGLGUIEvent.RemoveListener(const AListener: TGLGUIEventListener);
var
  i: Integer;
  vBind: TGLGUIEventListenerBind;
begin
  for i := 0 to FListeners.Count - 1 do
    if (@FListeners[i].Listener = @AListener) then
    begin
      vBind := FListeners[i];
      if FListeners.Remove(vBind) > -1 then
      begin
        vBind.Free;
        Exit;
      end;
    end;
end;

destructor TGLGUIEvent.Destroy;
var
  i: Integer;
begin
  for i := 0 to FListeners.Count - 1 do
    FListeners.Items[i].Free;
  FListeners.Free;
  inherited;
end;

procedure TGLGUISingleUserEvent.DispatchEvent(ASender: TObject);
var
  i: Integer;
begin
  FSender := ASender;
  for i := 0 to FListeners.Count - 1 do
    FListeners.Items[i].Listener(Self);
end;

procedure TGLGUIMouseEvent.Prepare;
begin
  GLInputManager.PrepareMouseEvent(Self);
end;

procedure TGLGUIProgressEvent.DispatchEvent(ASender: TObject);
var
  i: Integer;
begin
  FSender := ASender;
  for i := 0 to FListeners.Count - 1 do
    FListeners.Items[i].Listener(Self);
end;

initialization

RegisterEvent(TGLGUIMouseDownEvent);
RegisterEvent(TGLGUIMouseUpEvent);
RegisterEvent(TGLGUIMouseMoveEvent);
RegisterEvent(TGLGUIMouseEnterEvent);
RegisterEvent(TGLGUIMouseLeaveEvent);
RegisterEvent(TGLGUIMouseWheelEvent);
RegisterEvent(TGLGUIKeyDownEvent);
RegisterEvent(TGLGUIKeyUpEvent);
RegisterEvent(TGLGUIProgressEvent);

finalization

end.
